using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace BLT
{
    public class Menu
    {
        #region Type Definitions
        /// <summary>
        /// The function signature that the Game must use for options selected
        /// <param name="option">Upon calling, the name of the option selected</param>
        /// </summary>
        public delegate void optionDelegate(string option);
        #endregion

        #region Fields and their getters/setters

        /// <summary>
        /// The collection of the options shown on the menu
        /// </summary>
        public List<KeyValuePair<string, optionDelegate>> m_options
        {
            get { return options; }
            set { options = value; }
        }
        private List<KeyValuePair<string, optionDelegate>> options;

        /// <summary>
        /// The index of the active item in the Menu.
        /// </summary>
        public int m_activeItem
        {
            get { return activeItem; }
            set { activeItem = value; }
        }
        private int activeItem = 0;

        /// <summary>
        /// Where the Menu will be drawn (absolute coords. of upper left corner)
        /// </summary>
        public Vector2 m_location
        {
            get { return location; }
            set { location = value; }
        }
        private Vector2 location = new Vector2();

        /// <summary>
        /// Color to tint the text when drawn
        /// </summary>
        public Color m_textColor
        {
            get { return textColor; }
            set { textColor = value; }
        }
        private Color textColor = Color.White;

        /// <summary>
        /// Color to tint the background when drawn
        /// </summary>
        public Color m_backgroundColor
        {
            get { return backgroundColor; }
            set { backgroundColor = value; }
        }
        private Color backgroundColor = Color.White;

        /// <summary>
        /// X and Y size of the Menu
        /// </summary>
        public Vector2 m_size
        {
            get { return size; }
            set { size = value; }
        }
        private Vector2 size;

        /// <summary>
        /// The InputHandler that will contain the default keys for navigating a menu.
        /// (i.e. up/down, enter, mouse functions, etc.)
        /// </summary>
        private InputHandler m_inputHandler = new InputHandler();
        #endregion

        /// <summary>
        /// The title of the menu
        /// </summary>
        public String m_title
        {
            get { return title; }
            set { title = value; }
        }
        private String title;

        public SpriteFont m_font
        {
            get { return font; }
            set { font = value; }
        }
        private SpriteFont font;

        #region Constructors
        /// <summary>
        /// Creates a new menu, with default input handling functions.
        /// <param name="location">The location of the menu (upper-left corner)</param>
        /// <param name="size">The size of the menu</param>
        /// </summary>
        public Menu( Vector2 location, Vector2 size, SpriteFont font )
        {
            // HINT: Example of how to bind to input handler
            //m_inputHandler.Bind(Keys.Down, moveDown, 0);
            m_inputHandler.Bind(Keys.Down, new InputHandler.KeyboardInputAction(moveDown), 0);
            m_inputHandler.Bind(Keys.Up, new InputHandler.KeyboardInputAction(moveUp), 0);
            m_inputHandler.Bind(Keys.Enter, new InputHandler.KeyboardInputAction(selectItem), 0);

            options = new List<KeyValuePair<string,optionDelegate>>();
            m_activeItem = 0;
            m_font = font;

            m_location = location;
            m_size = size;
            m_textColor = Color.Black;
            m_backgroundColor = Color.White;
            m_title = "";
        }
        public Menu( Vector2 location, Vector2 size, Color textColor, Color backgroundColor, String title, SpriteFont font )
        {
            m_inputHandler.Bind(Keys.Down, new InputHandler.KeyboardInputAction(moveDown), 0);
            m_inputHandler.Bind(Keys.Up, new InputHandler.KeyboardInputAction(moveUp), 0);
            m_inputHandler.Bind(Keys.Enter, new InputHandler.KeyboardInputAction(selectItem), 0);

            options = new List<KeyValuePair<string, optionDelegate>>();
            m_activeItem = 0;
            m_font = font;

            m_location = location;
            m_size = size;
            m_textColor = textColor;
            m_backgroundColor = backgroundColor;
            m_title = title;
        }
        #endregion

        #region Other functions
        /// <summary>
        /// Adds the option/action pair into the list of options on the Menu
        /// <param name="option">The name of the option to add</param>
        /// <param name="action">The action that should be taken if that option is selected</param>
        /// </summary>
        public void addOption(string option, optionDelegate action)
        {
            m_options.Add(new KeyValuePair<string, optionDelegate>(option, action));
        }

        /// <summary>
        /// Inserts the option/action pair into the list of options on the Menu
        /// <param name="option">The name of the option to insert</param>
        /// <param name="action">The action that should be taken if that option is selected</param>
        /// <param name="index">The index at which to insert the option</param>
        /// </summary>
        public void insertOption(string option, optionDelegate action, int index)
        {
            m_options.Insert(index, new KeyValuePair<string, optionDelegate>(option, action));
        }

        /// <summary>
        /// Removes the option with the given name from the Menu
        /// <param name="option">The name of the option to remove</param>
        /// </summary>
        public void removeOption(string option)
        {
            for (int i = 0; i < m_options.Count; i++)
                if (m_options[i].Key.Equals(option))
                {
                    m_options.RemoveAt(i);
                    return;
                }
        }

        /// <summary>
        /// Checks inputs through the inputHandler (for navigating choices, selecting choices, etc.)
        /// </summary>
        public void update()
        {
            m_inputHandler.CheckInputs();
        }

        /// <summary>
        /// This function should be bound in the input handler to whatever button should move 
        /// the active item down on the Menu (i.e. down arrow).  It will then be called when the button is activated.
        /// <param name="state">State of the button</param>
        /// </summary>
        private void moveDown(Keys key, KeyState state)
        {
            if( state == KeyState.Down && m_activeItem != options.Count - 1 )
                m_activeItem++;
        }

        /// <summary>
        /// This function should be bound in the input handler to whatever button should move 
        /// the active item up on the Menu (i.e. up arrow).  It will then be called when the button is activated.
        /// <param name="state">State of the button</param>
        /// </summary>
        private void moveUp(Keys key, KeyState state)
        {
            if (state == KeyState.Down && m_activeItem != 0)
                m_activeItem--;
        }

        /// <summary>
        /// This function should be bound in the input handler to whatever button should select 
        /// the active item on the Menu (i.e. Enter).  It will then be called when the button is activated.
        /// <param name="state">State of the button</param>
        /// </summary>
        private void selectItem(Keys key, KeyState state)
        {
            if( state == KeyState.Down )
                options[m_activeItem].Value.Invoke(options[m_activeItem].Key);
        }

        /// <summary>
        /// Draws the Menu (highlighting the active item).
        /// </summary>
        public void draw(SpriteBatch sb)
        {
            
            //  Draw the title.
            sb.DrawString(font, m_title, m_location, m_textColor);

            //  Calculate where to position the menu items.
            float currentMenuItemX = m_location.X + 20;
            float currentMenuItemY = m_location.Y + 20;
            float menuItemPositionIncrement = (m_size.Y - 20) / m_options.Count;

            //  Draw each menu item
            for (int i = 0; i < options.Count; i++)
            {
                if (i == m_activeItem)
                    sb.DrawString(font, options[i].Key, new Vector2(currentMenuItemX, currentMenuItemY), m_textColor);
                else
                    sb.DrawString(font, m_options[i].Key, new Vector2(currentMenuItemX, currentMenuItemY), m_backgroundColor);
                currentMenuItemY += menuItemPositionIncrement;  //  Increment the Y position for the next item.
            }
        }
        #endregion
    }
}
